#!/usr/bin/env python
#go2_view.py

import gtk
import string

class View:
	
	def __init__( self, m ):
		
		self.editflag = False #does this belong in view?
		self.m = m
		
		# get images to use as expanders. set default size. user can customize images as desired
		# maybe tiny kittens would be nice
		# first commented line shows another way...embedded xpm data
			
		#pb_expander_full_closed = gtk.gdk.pixbuf_new_from_xpm_data(images.expander_full_closed)
		
		self.pb_expander_full_closed = gtk.gdk.pixbuf_new_from_file_at_size('image/expander_full_closed.png',12,12)
		self.pb_expander_empty_closed = gtk.gdk.pixbuf_new_from_file_at_size('image/expander_empty_closed.png',12,12)
		self.pb_expander_empty_open = gtk.gdk.pixbuf_new_from_file_at_size('image/expander_empty_open.png',12,12)
		self.io = ''
		
	def make_view(self):
		'''
		this is a little screwy. callbacks should be in the controller, not the
		view. But these cells depend on the tree column, which depends on the
		treeview, both of which are view properties. either this whole function 
		happens in the controller or the cell callbacks are handled here in the
		view. Leads to oddities such as having the buffer (keystrokes) stored
		here in the view instead of the controller because these cell callbacks
		need the buffer, even though keystrokes are handled in the controller!
		
		'''
		# create a CellRendererText to render the data
		self.cell = gtk.CellRendererText()
		self.cell.set_property('editable', True)
		self.cell.connect('edited', self.edited_cb)
		self.cell.connect('editing-started', self.editing_cb, self.m.treestore)    
		self.cell.connect('editing-canceled', self.editing_cancel_cb, self.treeview)
		# add the cell to the tvcolumn and allow it to expand
		
		self.tvcolumn.set_cell_data_func(self.cellpb,self.get_node_pixbuf, None)

		self.tvcolumn.pack_start(self.cell, False)
		# set the cell "text" attribute to column 0 - retrieve text
		# from that column in treestore
		self.tvcolumn.add_attribute(self.cell, 'text', 0)
		
		#the following is experimental to use with expanders turned off in glade file
		self.treeview.set_level_indentation(15)
		
		#set focus on first node
		self.treeview.set_cursor_on_cell((0,), self.tvcolumn, self.cell, True)
		
	def get_node_pixbuf(self, column, cell, model, iter, data=None):
		path = self.m.get_path(iter)
		child = self.m.treestore.iter_has_child(iter);
		if(child and self.treeview.row_expanded(path)):
			pb = self.pb_expander_empty_open
		elif(child):
			pb = self.pb_expander_full_closed
		else:
			pb = self.pb_expander_empty_closed
		cell.set_property('pixbuf', pb)
		return
	
	""" setter/getter functions """
	
	def set_treeview(self, tv):
		self.treeview = tv
		
	def set_column1(self, col):
		self.tvcolumn = col
		
	def set_cellpixbuf(self, pb):
		self.cellpb = pb
		
	def set_io(self, io):
		# hack to make console logging available here
		self.io = io
		
	def set_wintitle(self, title, win):
		win.set_title(title)
		
	def set_modified_callback(self, cb):
		self.modified_callback = cb
		
	def set_unsaved_dialog(self, dialog):
		self.unsaved_dialog = dialog
		
	""" outline functions """
	
	def indent(self, treestore, treeiter):
		newparent = self.get_sibling_previous_path(treestore, treeiter)
		if(newparent):
			self.io.info("new parent node sis %s" % newparent)
			piter = treestore.get_iter_from_string(newparent)
			self.io.info("piter is %s" % piter)
			data = treestore.get_value(treeiter, 0)
			
			nk = treestore.iter_n_children(piter)
			last = nk-1
			lastchild = treestore.iter_nth_child(piter, last)
			newiter = treestore.insert_after(piter,lastchild, [data])
			self.copy_subtree(treestore, treeiter, newiter)
			treestore.remove(treeiter)
			self.treeview.expand_to_path(treestore.get_path(newiter))
			self.focus(treestore, newiter)
			return(treestore, newiter)
		else:
			return False
			
	def outdent(self, treestore, treeiter):
		parent = treestore.iter_parent(treeiter)
		grandparent = treestore.iter_parent(parent)
		data = treestore.get_value(treeiter, 0)
		newiter = treestore.insert_after(grandparent, parent, [data])
		self.copy_subtree(treestore, treeiter, newiter)
		treestore.remove(treeiter)
		#if toplevel when outdenting, close former parent, now sibling
		self.focus(treestore, newiter)
		return(treestore, newiter)
		
	def move_up(self, treestore, treeiter):
		up_iter = self.get_sibling_previous_iter(treestore, treeiter)
		if(up_iter):
			#self.copy_subtree(treestore, treeiter, up_iter)
			treestore.move_before(treeiter, up_iter)
			self.focus(treestore, treeiter)
		else:
			self.focus(treestore, treeiter)
			
	def move_down(self, treestore, treeiter):
		dn_iter = self.get_sibling_next_iter(treestore, treeiter)
		if(dn_iter):
			#self.copy_subtree(treestore, treeiter, up_iter)
			treestore.move_after(treeiter, dn_iter)
			self.focus(treestore, treeiter)	
			
	def new_node(self, treestore, treeiter):
		self.io.info("got to new_node")
		parent = treestore.iter_parent(treeiter)
		newiter = treestore.insert_after(parent,treeiter)
		newpath = treestore.get_path(newiter)
		self.treeview.set_cursor_on_cell(newpath, self.tvcolumn, self.cell, True)
		return False # false means propagate keypress as needed
			
	def focus(self, treestore, treeiter):
		self.io.info("at focus...")
		selection = self.treeview.get_selection()
		selection.select_iter(treeiter)
		#self.treeview.set_cursor(treestore.get_path(treeiter),self.tvcolumn,True)
		self.treeview.set_cursor_on_cell(treestore.get_path(treeiter), self.tvcolumn, self.cell, False)
		
	def copy_subtree(self, treestore, from_iter, to_iter):
		if not treestore.iter_has_child(from_iter):
			return
		from_path = list(treestore.get_path(from_iter))
		
		for from_child_number in range(0, treestore.iter_n_children(from_iter)):
			from_path = list(treestore.get_path(from_iter))
			self.io.info("existing parent is %s" % from_path)
			from_child_path = from_path
			from_child_path.append(from_child_number)
			self.io.info("existing child path %s from child number %s" % (from_child_path, from_child_number))
			from_child = treestore.get_iter(tuple(from_child_path))
			to_child = treestore.append(to_iter, row=treestore[tuple(from_child_path)])
			self.copy_subtree(treestore, from_child, to_child)
			from_child_path = ''
			
	def get_sibling_previous_path(self, treestore, treeiter):
		#returns a string rep of path, not gtk.TreeIter
		parent = treestore.iter_parent(treeiter)
		numkids = treestore.iter_n_children(parent)
		path = treestore.get_path(treeiter)
		strpath = treestore.get_string_from_iter(treeiter)
		mynum = path[-1]
		
		if(numkids>1): #we have siblings
			if (mynum>0):
				#this iter is not the first iter, we have previous siblings
				listpath = string.split(strpath, ':')
				listpath.pop()
				listpath.append(str(mynum-1))
				listpath = string.join(listpath, ':')
				return(listpath)
		
		else:
			#mynum == 0, no previous siblings
				return(False)
		self.io.info("num kids is %i and mynum is %i and strpath is %s" % \
		(numkids, mynum, strpath))
	
	def get_sibling_previous_iter(self, treestore, treeiter):
		previous = self.get_sibling_previous_path(treestore, treeiter)
		if(previous):
			return(treestore.get_iter_from_string(previous))
		
	def get_sibling_next_path(self, treestore, treeiter):
		#returns a string rep of path, not gtk.TreeIter
		next = treestore.iter_next(treeiter)
		return(treestore.get_path(next))
		
	def get_sibling_next_iter(self, treestore, treeiter):
		#returns a string rep of path, not gtk.TreeIter
		return (treestore.iter_next(treeiter))
	

		""" callbacks section """
	
	def edited_cb(self, cell, path, new_text):
		#changing data - should handle via model method?
		treestore = self.treeview.get_model()
		treestore[path][0] = new_text
		self.editflag = False
		self.io.info("edited_cb - editing complete at path %s" % ( path ))
		return
		
	def editing_cb(self, cell, editable, path, treestore):
		# TODO: for some reason, the treestore passed by this function can 
		# refer to an older treestore if you opened a new file, so get correct
		treestore = self.treeview.get_model()
		#call internal callback to set dirty buffer
		self.modified_callback()
		
		curtext = treestore[path][0]
		curtext = treestore.get_value(treestore.get_iter(path),0)
		self.io.info("curtext is %s" % curtext)
		buff = editable.get_buffer() #only discovered editable through debugging
		if (buff.get_text() == '' and curtext != None):
			buff.set_text(curtext, len(curtext) )
			self.io.info("setting buffer text to %s" % curtext)
		else:
			self.io.info("using existing buffer text: %s" % buff.get_text())
			
		# TODO: attempt to set cursor at end of buffer - does not work currently
		buff.ln = buff.get_length()
		editable.set_position(buff.ln)
		#self.start_editing(path, buff)
		self.editflag = True
		self.editpath = path
		self.editbuffer = buff

		self.io.info("start_editing")
		
		#experiment on emiting key press event starts below
		"""
		win = self.treeview.get_root_window()
		event = gtk.gdk.Event(gtk.gdk.KEY_PRESS)
		event.keyval = gtk.keysyms.Right
		event.state = gtk.gdk.CONTROL_MASK
		event.time = 0 # assign current time
		
		#win.emit('key_press_event', event)
		"""
	
		
	def editing_cancel_cb(self, cell, treeview):
		#escape key is legitimate cancel, handled in go2_keyfunc.py
		#for now, anything else that sends us here results in a saved buffer
		self.io.info("editing cancelled!")
		
		self.save_and_stop_editing()
		self.editflag=False
		self.buffer=''

	def stop_editing(self):
		self.editflag = False
		self.editpath=''
		self.editbuffer = ''
		self.io.info("stop_editing")
		
	def save_and_stop_editing(self, key=''):
		self.m.treestore[self.editpath][0] = self.editbuffer.get_text()
		self.editflag = False
		self.editpath=''
		self.editbuffer = ''
		self.io.info("save_stop_editing"	)
	
	
	""" dialogs section """
	def open_unsaved_dialog(self):
		response = self.unsaved_dialog.run()
		self.unsaved_dialog.hide()
		return response
		
	def open_filechooser(self):
		dialog = gtk.FileChooserDialog("Open..",
							  None,
							  gtk.FILE_CHOOSER_ACTION_OPEN,
							  (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
							   gtk.STOCK_OPEN, gtk.RESPONSE_OK))
		dialog.set_default_response(gtk.RESPONSE_OK)
		filter = gtk.FileFilter()
		filter.set_name(".xml")
		filter.add_pattern("*.xml")
		dialog.add_filter(filter)
		
		response = dialog.run()
		if response == gtk.RESPONSE_OK:
			fpath = dialog.get_filename()
			self.io.info('%s selected' % fpath)
		elif response == gtk.RESPONSE_CANCEL:
			self.io.info('Closed, no files selected')
		dialog.destroy()
		return(fpath)
	
	def saveas_filechooser(self):
		dialog = gtk.FileChooserDialog("Save..",
							  None,
							  gtk.FILE_CHOOSER_ACTION_SAVE,
							  (gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
							   gtk.STOCK_SAVE, gtk.RESPONSE_OK))
		dialog.set_default_response(gtk.RESPONSE_OK)
		filter = gtk.FileFilter()
		filter.set_name(".xml")
		filter.add_pattern("*.xml")
		dialog.add_filter(filter)
		filter = gtk.FileFilter()
		filter.set_name("all files")
		filter.add_pattern("*")
		dialog.add_filter(filter)
		
		response = dialog.run()
		if response == gtk.RESPONSE_OK:
			fpath = dialog.get_filename()
			self.io.info('%s selected' % fpath)
		elif response == gtk.RESPONSE_CANCEL:
			self.io.info('Closed, no files selected')
		dialog.destroy()
		if(fpath):
			return(fpath)
